#!/bin/bash
# valheim-backups runs permanently if BACKUPS=true
# and creates backups of the /config/worlds_local directory.

# Include defaults
. /usr/local/etc/valheim/defaults
. /usr/local/etc/valheim/common

# Remove trailing slash if any
BACKUPS_DIRECTORY=${BACKUPS_DIRECTORY%/}
pidfile=$valheim_backup_pidfile
next_backup=$(date +%s)
run=true


main() {
    local last_activity
    last_activity=$(date +%s)
    if (set -o noclobber; echo $$ > "$pidfile") 2> /dev/null; then
        trap backup_now SIGHUP
        trap shutdown SIGINT SIGTERM
        trap 'error_handler $? $LINENO $BASH_LINENO "$BASH_COMMAND" $(printf "::%s" ${FUNCNAME[@]}); trap - ERR' ERR

        cd /config || fatal "Could not cd /config"
        while [ $run = true ]; do
            if [ "$BACKUPS_IF_IDLE" = true ] || [ "$(date +%s)" -le $((last_activity+BACKUPS_IDLE_GRACE_PERIOD)) ]; then
                backup
                flush_old
            else
                debug "Not running backup as there has been no server activity since $(date -d @"$last_activity")"
            fi
            next_backup=$(($(date +%s)+BACKUPS_INTERVAL))
            while [ $run = true ] && [ "$(date +%s)" -lt $next_backup ]; do
                sleep 20
                if [ "$BACKUPS_IF_IDLE" = false ] && ! server_is_idle; then
                    last_activity=$(date +%s)
                fi
            done
        done
    else
        info "Found existing pid file - checking process"
        check_lock "$pidfile"
    fi
}


backup() {
    local backup_file
    local WORLDS_DIR
    if [ -d "$worlds_dir" ]; then
        WORLDS_DIR=$worlds_dir
    elif [ -d "$old_worlds_dir" ]; then
        WORLDS_DIR=$old_worlds_dir
    else
        info "No worlds to backup"
        return
    fi

    mkdir -p "$BACKUPS_DIRECTORY"
    chmod "$BACKUPS_DIRECTORY_PERMISSIONS" "$BACKUPS_DIRECTORY"

    if [ "${BACKUPS_ZIP}" = true ]; then
        backup_file="$BACKUPS_DIRECTORY/worlds-$(date +%Y%m%d-%H%M%S).zip"
        pre_backup_hook "$backup_file"
        info "Backing up Valheim server worlds to $backup_file"
        zip -r "$backup_file" "$WORLDS_DIR/" -x '*_backup_auto-*.*'
        chmod "$BACKUPS_FILE_PERMISSIONS" "$backup_file"
        post_backup_hook "$backup_file"
    else
        local world_file
        local backup_suffix
        backup_suffix=$(date +%Y%m%d-%H%M%S)

        for extension in {db,fwl,db.old,fwl.old}; do
            world_file="$WORLDS_DIR/$WORLD_NAME.$extension"
            if [ ! -f "$world_file" ]; then
                debug "World file $world_file not found - skipping"
                continue
            fi
            backup_file="$BACKUPS_DIRECTORY/AUTOBACKUP-${WORLD_NAME}-$backup_suffix.$extension"
            pre_backup_hook "$backup_file"
            info "Backing up Valheim server world file $world_file to $backup_file"
            cp -v "$world_file" "$backup_file"
            chmod "$BACKUPS_FILE_PERMISSIONS" "$backup_file"
            post_backup_hook "$backup_file"
        done
    fi
}


pre_backup_hook() {
    local backup_file
    local pre_hook_cmd
    if [ -n "$PRE_BACKUP_HOOK" ]; then
        backup_file=$1
        pre_hook_cmd=${PRE_BACKUP_HOOK//@BACKUP_FILE@/$backup_file}
        info "Running pre backup hook: $pre_hook_cmd"
        eval "$pre_hook_cmd"
    fi
}


post_backup_hook() {
    local backup_file
    local post_hook_cmd
    if [ -n "$POST_BACKUP_HOOK" ]; then
        backup_file=$1
        post_hook_cmd=${POST_BACKUP_HOOK//@BACKUP_FILE@/$backup_file}
        info "Running post backup hook: $post_hook_cmd"
        eval "$post_hook_cmd"
    fi
}


flush_old() {
    if [ ! -d "$BACKUPS_DIRECTORY" ]; then
        debug "No old backups to remove"
        return
    fi

    if [ "$BACKUPS_MAX_COUNT" -gt 0 ]; then
        info "Removing all but the newest $BACKUPS_MAX_COUNT backups"
        if [ "${BACKUPS_ZIP}" = true ]; then
            find "$BACKUPS_DIRECTORY" -type f -name "worlds-*.zip" -printf '%T@ %p\0' | sort -z -n -r | cut -z -s -d " " -f "2-" | tail -z -n +$((BACKUPS_MAX_COUNT+1)) | xargs -0 rm -fv -- 2>/dev/null
        else
            for extension in {db,fwl,db.old,fwl.old}; do
                find "$BACKUPS_DIRECTORY" -type f -name "AUTOBACKUP-${WORLD_NAME}-*.${extension}" -printf '%T@ %p\0' | sort -z -n -r | cut -z -s -d " " -f "2-" | tail -z -n +$((BACKUPS_MAX_COUNT+1)) | xargs -0 rm -v -- 2>/dev/null
            done
        fi
    fi

    info "Removing backups older than $BACKUPS_MAX_AGE days"
    if [ "${BACKUPS_ZIP}" = true ]; then
        find "$BACKUPS_DIRECTORY" -type f -mmin "+$((BACKUPS_MAX_AGE*60*24))" -name "worlds-*.zip" -print -exec rm -f "{}" \;
    else
        for extension in {db,fwl,db.old,fwl.old}; do
            find "$BACKUPS_DIRECTORY" -type f -mmin "+$((BACKUPS_MAX_AGE*60*24))" -name "AUTOBACKUP-${WORLD_NAME}-*.${extension}" -print -exec rm -f "{}" \;
        done
    fi
}


# This is a signal handler registered to SIGHUP
backup_now() {
    debug "Received signal to backup world"
    next_backup=0
}


shutdown() {
    debug "Received signal to shut down valheim-backup"
    clear_lock "$pidfile"
    run=false
}


if [ "$BACKUPS" = true ]; then
    main
else
    info "Backups have been turned off by env BACKUPS=$BACKUPS"
    supervisorctl stop valheim-backup
fi
